
package org.apache.ocean;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.DisjunctionMaxQuery;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FilteredQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.RangeFilter;
import org.apache.lucene.search.RangeQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.similar.MoreLikeThisQuery;
import org.apache.lucene.search.spans.SpanFirstQuery;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanNotQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.jdom.Element;


/**
 *
 * @author  jason rutherglen
 */
public class XMLQueryUtil {
  public static Logger log = Logger.getLogger(XMLQueryUtil.class.getName());
  
  public XMLQueryUtil() {
  }
  
  public static String toString(Query query) {
    if (query == null) return "null";
    if (query instanceof SpanNearQuery) {
      SpanNearQuery spanNearQuery = (SpanNearQuery)query;
      
      String str = "SpanNearQuery field: "+spanNearQuery.getField()+" slop: "+spanNearQuery.getSlop()+" isInOrder(): "+spanNearQuery.isInOrder();
      SpanQuery[] clauses = spanNearQuery.getClauses();
      List list = new ArrayList(clauses.length);
      for (int x=0; x < clauses.length; x++) {
        list.add(toString(clauses[x]));
      }
      str = str+" clauses: "+list;
      return str;
    } else if (query instanceof SpanFirstQuery) {
      SpanFirstQuery spanFirstQuery = (SpanFirstQuery)query;
      
      int end = spanFirstQuery.getEnd();
      String field = spanFirstQuery.getField();
      SpanQuery spanQuery = spanFirstQuery.getMatch();
      
      return "SpanFirstQuery field: "+field+" end: "+end+" spanQuery: "+toString(spanQuery);
    } else if (query instanceof SpanTermQuery) {
      SpanTermQuery spanTermQuery = (SpanTermQuery)query;
      
      String field = spanTermQuery.getField();
      Term term = spanTermQuery.getTerm();
      
      return "SpanFirstQuery field: "+field+" term: "+term;
    } else {
      return query.toString();
    }
  }
  
  public static Element toElement(Query query) {
    if (query instanceof BooleanQuery) {
      BooleanQuery booleanQuery = (BooleanQuery)query;
      
      return toElement(booleanQuery);
    } else if (query instanceof FilteredQuery) {
      FilteredQuery filteredQuery = (FilteredQuery)query;
      
      return toElement(filteredQuery);
    } else if (query instanceof TermQuery) {
      TermQuery termQuery = (TermQuery)query;
      
      return toElement(termQuery);
    } else if (query instanceof SpanOrQuery) {
      SpanOrQuery spanOrQuery = (SpanOrQuery)query;
      
      return toElement(spanOrQuery);
    } else if (query instanceof SpanNotQuery) {
      SpanNotQuery spanNotQuery = (SpanNotQuery)query;
      
      return toElement(spanNotQuery);
    } else if (query instanceof SpanTermQuery) {
      SpanTermQuery spanTermQuery = (SpanTermQuery)query;
      
      return toElement(spanTermQuery);
    } else if (query instanceof SpanFirstQuery) {
      SpanFirstQuery spanFirstQuery = (SpanFirstQuery)query;
      
      return toElement(spanFirstQuery);
    } else if (query instanceof SpanNearQuery) {
      SpanNearQuery spanNearQuery = (SpanNearQuery)query;
      
      return toElement(spanNearQuery);
    } else if (query instanceof ConstantScoreQuery) {
      ConstantScoreQuery constantScoreQuery = (ConstantScoreQuery)query;
      
      return toElement(constantScoreQuery);
    } else if (query instanceof RangeQuery) {
      RangeQuery rangeQuery = (RangeQuery)query;
      
      return toElement(rangeQuery);
    } else if (query instanceof MoreLikeThisQuery) {
      MoreLikeThisQuery moreLikeThisQuery = (MoreLikeThisQuery)query;
      
      return toElement(moreLikeThisQuery);
    } else if (query instanceof DisjunctionMaxQuery) {
      DisjunctionMaxQuery disjunctionMaxQuery = (DisjunctionMaxQuery)query;
      
      return toElement(disjunctionMaxQuery);
    }
    throw new IllegalArgumentException("query "+query.getClass().getName()+" unsupported");
  }
  
  public static Element toElement(MoreLikeThisQuery moreLikeThisQuery) {
    String[] moreLikeFields = moreLikeThisQuery.getMoreLikeFields();
    String likeText = moreLikeThisQuery.getLikeText();
    int maxQueryTerms = moreLikeThisQuery.getMaxQueryTerms();
    int minTermFrequency = moreLikeThisQuery.getMinTermFrequency();
    float percentTermsToMatch = moreLikeThisQuery.getPercentTermsToMatch();
    
    Element moreLikeThisQueryElement = new Element("MoreLikeThisQuery");
    moreLikeThisQueryElement.setAttribute("maxQueryTerms", Integer.toString(maxQueryTerms));
    moreLikeThisQueryElement.setAttribute("minTermFrequency", Integer.toString(minTermFrequency));
    moreLikeThisQueryElement.setAttribute("percentTermsToMatch", Integer.toString((int)(percentTermsToMatch*100)));
    moreLikeThisQueryElement.setAttribute("boost", Float.toString(moreLikeThisQuery.getBoost()));
    
    String fieldNames = org.apache.commons.lang.StringUtils.join(moreLikeFields, ",");
    moreLikeThisQueryElement.setAttribute("fieldNames", fieldNames);
    
    moreLikeThisQueryElement.setText(likeText);
    
    //XMLUtil.createTextElement("likeText", likeText, moreLikeThisQueryElement);
    //XMLUtil.createTextElement("maxQueryTerms", Integer.toString(maxQueryTerms), moreLikeThisQueryElement);
    //XMLUtil.createTextElement("minTermFrequency", Integer.toString(minTermFrequency), moreLikeThisQueryElement);
    //XMLUtil.createTextElement("percentTermsToMatch", Float.toString(percentTermsToMatch), moreLikeThisQueryElement);
    
    //for (int x=0; x < moreLikeFields.length; x++) {
    //  XMLUtil.createTextElement("field", moreLikeFields[x], moreLikeThisQueryElement);
    //}
    
    return moreLikeThisQueryElement;
  }
  
  public static Element toElement(DisjunctionMaxQuery disjunctionMaxQuery) {
    Element disjunctionMaxQueryElement = new Element("DisjunctionMaxQuery");
    
    float boost = disjunctionMaxQuery.getBoost();
    disjunctionMaxQueryElement.setAttribute("boost", Float.toString(boost));
    //float tieBreakerMultiplier = FieldUtil.getFieldValue("tieBreakerMultiplier", disjunctionMaxQuery);//disjunctionMaxQuery.tieBreakerMultiplier;
    try {
      Float tieBreakerMultiplierFloat = (Float)FieldUtil.getFieldValue("tieBreakerMultiplier", disjunctionMaxQuery);
      if (tieBreakerMultiplierFloat != null) {
        disjunctionMaxQueryElement.setAttribute("tieBreakerMultiplier", tieBreakerMultiplierFloat.toString());
      }
    } catch (Throwable throwable) {
      log.log(Level.SEVERE, "", throwable);
    }
    Iterator disjunctionsIterator = disjunctionMaxQuery.iterator();
    while (disjunctionsIterator.hasNext()) {
      Query query = (Query)disjunctionsIterator.next();
      Element queryElement = toElement(query);
      disjunctionMaxQueryElement.addContent(queryElement);
    }
    return disjunctionMaxQueryElement;
  }
  
  public static Element toElement(RangeQuery rangeQuery) {
    Term lowerTerm = rangeQuery.getLowerTerm();
    Term upperTerm = rangeQuery.getUpperTerm();
    String field = rangeQuery.getField();
    
    Element rangeQueryElement = new Element("RangeQuery");
    rangeQueryElement.setAttribute("field", field);
    rangeQueryElement.setAttribute("lowerTerm", lowerTerm.text());
    rangeQueryElement.setAttribute("upperTerm", upperTerm.text());
    rangeQueryElement.setAttribute("isInclusive", Boolean.toString(rangeQuery.isInclusive()));
    
    float boost = rangeQuery.getBoost();
    rangeQueryElement.setAttribute("boost", Float.toString(boost));
    
    return rangeQueryElement;
  }
  
  public static Element toElement(RangeFilter rangeFilter) {
    //rangeFilter.
    return null;
    /**
     * String fieldName=DOMUtils.getAttributeWithInheritance(e,"fieldName");
     *
     * String lowerTerm=e.getAttribute("lowerTerm");
     * String upperTerm=e.getAttribute("upperTerm");
     * boolean includeLower=DOMUtils.getAttribute(e,"includeLower",true);
     * boolean includeUpper=DOMUtils.getAttribute(e,"includeUpper",true);
     **/
  }
  
  //public static Element toElement(BooleanFilter booleanFilter) {
  //  return null;
  //}
  
  public static Element toElement(ConstantScoreQuery constantScoreQuery) {
    Element constantScoreQueryElement = new Element("ConstantScoreQuery");
    
    //constantScoreQuery.filter.
    
    return constantScoreQueryElement;
  }
  
  public static void setFieldAttribute(Element element, SpanQuery spanQuery) {
    String field = spanQuery.getField();
    if (!org.apache.commons.lang.StringUtils.isBlank(field)) {
      element.setAttribute("fieldName", field);
    }
  }
  
  public static Element toElement(SpanNearQuery spanNearQuery) {
    Element spanNearQueryElement = new Element("SpanNear");
    
    setFieldAttribute(spanNearQueryElement, spanNearQuery);
    
    float boost = spanNearQuery.getBoost();
    spanNearQueryElement.setAttribute("boost", Float.toString(boost));
    
    int slop = spanNearQuery.getSlop();
    spanNearQueryElement.setAttribute("slop", Integer.toString(slop));
    
    boolean isInOrder = spanNearQuery.isInOrder();
    spanNearQueryElement.setAttribute("inOrder", Boolean.toString(isInOrder));
    
    SpanQuery[] clauses = spanNearQuery.getClauses();
    for (int x=0; x < clauses.length; x++) {
      Element clauseElement = toElement(clauses[x]);
      
      spanNearQueryElement.addContent(clauseElement);
    }
    return spanNearQueryElement;
  }
  
  public static Element toElement(SpanOrQuery spanOrQuery) {
    Element spanOrQueryElement = new Element("SpanOr");
    
    setFieldAttribute(spanOrQueryElement, spanOrQuery);
    
    float boost = spanOrQuery.getBoost();
    spanOrQueryElement.setAttribute("boost", Float.toString(boost));
    
    SpanQuery[] clauses = spanOrQuery.getClauses();
    for (int x=0; x < clauses.length; x++) {
      Element clauseElement = toElement(clauses[x]);
      
      spanOrQueryElement.addContent(clauseElement);
    }
    return spanOrQueryElement;
  }
  
  public static Element toElement(TermQuery termQuery) {
    Element termQueryElement = new Element("TermQuery");
    
    float boost = termQuery.getBoost();
    termQueryElement.setAttribute("boost", Float.toString(boost));
    Term term = termQuery.getTerm();
    XMLUtil.setAttribute("fieldName", term.field(), termQueryElement);
    termQueryElement.setText(term.text());
    
    return termQueryElement;
  }
  
  public static Element toElement(SpanNotQuery spanNotQuery) {
    Element spanNotElement = new Element("SpanNot");
    
    setFieldAttribute(spanNotElement, spanNotQuery);
    
    SpanQuery exclude = spanNotQuery.getExclude();
    Element excludeElement = new Element("Exclude");
    excludeElement.addContent(toElement(exclude));
    
    spanNotElement.addContent(excludeElement);
    
    SpanQuery include = spanNotQuery.getInclude();
    Element includeElement = new Element("Include");
    includeElement.addContent(toElement(include));
    
    spanNotElement.addContent(includeElement);
    
    float boost = spanNotQuery.getBoost();
    spanNotElement.setAttribute("boost", Float.toString(boost));
    
    return spanNotElement;
  }
  
  public static Element toElement(SpanFirstQuery spanFirstQuery) {
    Element spanFirstElement = new Element("SpanFirst");
    
    setFieldAttribute(spanFirstElement, spanFirstQuery);
    
    int end = spanFirstQuery.getEnd();
    float boost = spanFirstQuery.getBoost();
    SpanQuery spanQuery = spanFirstQuery.getMatch();
    
    spanFirstElement.setAttribute("end", Integer.toString(end));
    spanFirstElement.setAttribute("boost", Float.toString(boost));
    
    spanFirstElement.addContent(toElement(spanQuery));
    
    return spanFirstElement;
  }
  
  public static Element toElement(SpanTermQuery spanTermQuery) {
    Element spanTermQueryElement = new Element("SpanTerm");
    
    setFieldAttribute(spanTermQueryElement, spanTermQuery);
    
    Term term = spanTermQuery.getTerm();
    
    float boost = spanTermQuery.getBoost();
    spanTermQueryElement.setText(term.text());
    String field = term.field();
    if (!org.apache.commons.lang.StringUtils.isBlank(field)) {
      spanTermQueryElement.setAttribute("field", field);
    }
    spanTermQueryElement.setAttribute("boost", Float.toString(boost));
    
    return spanTermQueryElement;
  }
  
  public static Element toElement(FilteredQuery filteredQuery) {
    Element filteredQueryElement = new Element("FilteredQuery");
    
    Filter filter = filteredQuery.getFilter();
    //filter.
    
    float boost = filteredQuery.getBoost();
    filteredQueryElement.setAttribute("boost", Float.toString(boost));
    return filteredQueryElement;
  }
  
  public static Element toElement(BooleanQuery booleanQuery) {
    Element booleanQueryElement = new Element("BooleanQuery");
    
    float boost = booleanQuery.getBoost();
    booleanQueryElement.setAttribute("boost", Float.toString(boost));
    BooleanClause[] clauses = booleanQuery.getClauses();
    for (int x=0; x < clauses.length; x++) {
      BooleanClause.Occur occur = clauses[x].getOccur();
      
      Element clauseQueryElement = toElement(clauses[x].getQuery());
      
      Element clauseElement = new Element("Clause");
      clauseElement.addContent(clauseQueryElement);
      
      if (occur.equals(BooleanClause.Occur.MUST)) {
        clauseElement.setAttribute("occurs", "must");
      } else if (occur.equals(BooleanClause.Occur.MUST_NOT)) {
        clauseElement.setAttribute("occurs", "mustnot");
      } else if (occur.equals(BooleanClause.Occur.SHOULD)) {
        clauseElement.setAttribute("occurs", "should");
      }
      booleanQueryElement.addContent(clauseElement);
    }
    return booleanQueryElement;
  }
  
  /**
   * public Query getQuery(Element e) throws ParserException {
   * BooleanQuery bq=new BooleanQuery();
   * bq.setBoost(DOMUtils.getAttribute(e,"boost",1.0f));
   * NodeList nl = e.getElementsByTagName("Clause");
   * for(int i=0;i<nl.getLength();i++)
   * {
   * Element clauseElem=(Element) nl.item(i);
   * BooleanClause.Occur occurs=getOccursValue(clauseElem);
   *
   * Element clauseQuery=DOMUtils.getFirstChildOrFail(clauseElem);
   * Query q=factory.getQuery(clauseQuery);
   * bq.add(new BooleanClause(q,occurs));
   * }
   *
   * return bq;
   * }
   **/
}
